package com.getan.boot.module.system.service.persistent;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.getan.boot.common.enums.EnableStatus;
import com.getan.boot.common.utils.CollectionUtils;
import com.getan.boot.common.utils.Preconditions;
import com.getan.boot.common.utils.StringPool;
import com.getan.boot.common.utils.StringUtils;
import com.getan.boot.jdbc.service.GenericService;
import com.getan.boot.module.system.constant.Constants;
import com.getan.boot.module.system.constant.ErrorCode;
import com.getan.boot.module.system.constant.UserTypeEnum;
import com.getan.boot.module.system.entity.ConfigEntity;
import com.getan.boot.module.system.entity.UserDeptEntity;
import com.getan.boot.module.system.entity.UserEntity;
import com.getan.boot.module.system.entity.UserRoleEntity;
import com.getan.boot.module.system.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 用户
 *
 * @author Bruce.Gong
 * @since 1.0.0-SNAPSHOT
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class UserService extends GenericService<UserRepository, UserEntity> {

    private final UserRoleService userRoleService;
    private final UserDeptService userDeptService;
    private final ConfigService configService;

    /**
     * 校验用户名是否唯一
     *
     * @param entity {username}
     */
    public void checkUsername(UserEntity entity) {
        Preconditions.check(() -> exists(Wrappers.<UserEntity>lambdaQuery()
                .eq(UserEntity::getUsername, entity.getUsername())), ErrorCode.USER_NAME_EXISTS);
    }

    /**
     * 校验手机号是否唯一
     *
     * @param entity {mobile}
     */
    public void checkMobile(UserEntity entity) {
        if (StringUtils.isNoneEmpty(entity.getMobile())) {
            Preconditions.check(() -> exists(Wrappers.<UserEntity>lambdaQuery()
                    .eq(UserEntity::getMobile, entity.getMobile())), ErrorCode.USER_MOBILE_EXISTS);
        }
    }

    private UserEntity checkExists(Long id) {
        UserEntity db = getById(id);
        Preconditions.isNull(db, ErrorCode.USER_NOT_EXISTS);
        return db;
    }

    public List<UserEntity> checkExists(List<Long> ids) {
        List<UserEntity> db = listByIds(ids);
        Preconditions.isNull(db, ErrorCode.USER_NOT_EXISTS);
        return db;
    }

    /**
     * 判断是否可修改
     *
     * @param id 用户ID
     * @return 用户信息
     */
    public UserEntity checkActive(Long id) {
        UserEntity db = checkExists(id);
        Preconditions.check(() -> EnableStatus.DISABLE.match(db.getStatus()), ErrorCode.USER_DISABLED);
        return db;
    }

    /**
     * 判断是否可删除
     *
     * @param ids 用户ID
     */
    public List<UserEntity> canDelete(List<Long> ids) {
        return checkExists(ids);
    }

    /**
     * 检查字典是否停用
     *
     * @param dbs 字典项
     */
    public void checkDisabled(List<UserEntity> dbs) {
        List<String> codes = dbs.stream()
                .filter(v -> EnableStatus.DISABLE.match(v.getStatus()))
                .map(UserEntity::getUsername)
                .collect(Collectors.toList());
        Preconditions.notEmpty(codes, ErrorCode.USER_IS_DISABLED, codes);
    }

    /**
     * 检查字典是否启用
     *
     * @param dbs 字典项
     */
    public void checkEnable(List<UserEntity> dbs) {
        List<String> codes = dbs.stream()
                .filter(v -> EnableStatus.ENABLE.match(v.getStatus()))
                .map(UserEntity::getUsername)
                .collect(Collectors.toList());
        Preconditions.notEmpty(codes, ErrorCode.USER_IS_ENABLE, codes);
    }

    /**
     * 检查用户是否可修改
     *
     * @param entity 用户
     * @return 用户信息
     */
    public UserEntity canUpdate(UserEntity entity) {
        UserEntity db = checkActive(entity.getId());
        // 手机号发生变化，保证唯一
        if (!Objects.equals(db.getMobile(), entity.getMobile())) {
            checkMobile(entity);
        }
        return db;
    }

    /**
     * 检查是否可停用
     *
     * @param ids 用户ID
     * @return 用户信息
     */
    public List<UserEntity> canDisable(List<Long> ids) {
        List<UserEntity> dbs = checkExists(ids);
        checkDisabled(dbs);
        return dbs;
    }

    /**
     * 检查是否可启用
     *
     * @param ids 用户ID
     * @return 用户信息
     */
    public List<UserEntity> canEnable(List<Long> ids) {
        List<UserEntity> dbs = checkExists(ids);
        checkEnable(dbs);
        return dbs;
    }

    /**
     * 新增用户
     *
     * @param entity  用户信息
     * @param deptIds 部门信息
     * @param roleIds 角色信息
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void addUser(UserEntity entity, List<Long> deptIds, List<Long> roleIds) {
        entity.setStatus(EnableStatus.ENABLE.getCode());
        entity.setType(UserTypeEnum.COMMON.getCode());
        entity.setSalt(StringUtils.substring(StringUtils.uuid(), 0, 8));
        // 设置默认密码
        ConfigEntity config = configService.getByCode(Constants.CONFIG_DEFAULT_PASSWORD);
        String password = Optional.ofNullable(config).map(ConfigEntity::getValue).orElse(Constants.DEFAULT_PASSWORD);
        password = DigestUtils.md5Hex(password);
        entity.setPassword(DigestUtils.sha256Hex(password + entity.getSalt()));
        insert(entity);
        // 分配部门
        allocateDept(entity.getId(), deptIds);
        // 分配角色
        allocateRole(entity.getId(), roleIds);
    }

    /**
     * 分配角色
     *
     * @param userId  用户ID
     * @param roleIds 角色ID
     */
    private void allocateRole(Long userId, List<Long> roleIds) {
        if (CollectionUtils.isEmpty(roleIds)) {
            return;
        }
        List<UserRoleEntity> insertList = roleIds.stream()
                .map(v -> UserRoleEntity.builder()
                        .userId(userId)
                        .roleId(v)
                        .build())
                .collect(Collectors.toList());
        userRoleService.insertBatch(insertList);
    }

    /**
     * 分配部门
     *
     * @param userId  用户ID
     * @param deptIds 角色ID
     */
    private void allocateDept(Long userId, List<Long> deptIds) {
        if (CollectionUtils.isEmpty(deptIds)) {
            return;
        }
        List<UserDeptEntity> insertList = deptIds.stream()
                .map(v -> UserDeptEntity.builder()
                        .userId(userId)
                        .deptId(v)
                        .build())
                .collect(Collectors.toList());
        userDeptService.insertBatch(insertList);
    }

    /**
     * 修改用户信息
     *
     * @param entity  用户信息
     * @param deptIds 部门
     * @param roleIds 角色
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void updateUser(UserEntity entity, List<Long> deptIds, List<Long> roleIds) {
        update(entity);
        // 调整部门
        updateDept(entity.getId(), deptIds);
        // 调整角色
        updateRole(entity.getId(), roleIds);
    }

    /**
     * 调整角色
     *
     * @param userId  用户ID
     * @param roleIds 角色ID
     */
    private void updateRole(Long userId, List<Long> roleIds) {
        if (CollectionUtils.isEmpty(roleIds)) {
            deleteRoleByUserId(userId);
        } else {
            Set<Long> dbRoleIds = selectRoleIds(userId);
            if (CollectionUtils.isEmpty(dbRoleIds)) {
                allocateRole(userId, roleIds);
            } else {
                roleIds.removeIf(dbRoleIds::remove);
                allocateRole(userId, roleIds);
                if (CollectionUtils.isNotEmpty(dbRoleIds)) {
                    userRoleService.delete(Wrappers.<UserRoleEntity>lambdaQuery()
                            .eq(UserRoleEntity::getUserId, userId)
                            .in(UserRoleEntity::getRoleId, dbRoleIds));
                }
            }
        }
    }

    /**
     * 调整部门
     *
     * @param userId  用户ID
     * @param deptIds 部门ID
     */
    private void updateDept(Long userId, List<Long> deptIds) {
        if (CollectionUtils.isEmpty(deptIds)) {
            deleteDeptByUserId(userId);
        } else {
            Set<Long> dbDeptIds = selectDeptIds(userId);
            if (CollectionUtils.isEmpty(dbDeptIds)) {
                allocateDept(userId, deptIds);
            } else {
                deptIds.removeIf(dbDeptIds::remove);
                allocateDept(userId, deptIds);
                if (CollectionUtils.isNotEmpty(dbDeptIds)) {
                    userDeptService.delete(Wrappers.<UserDeptEntity>lambdaQuery()
                            .eq(UserDeptEntity::getUserId, userId)
                            .in(UserDeptEntity::getDeptId, dbDeptIds));
                }
            }
        }
    }

    /**
     * 根据用户ID删除部门信息
     *
     * @param userIds 用户ID
     */
    private void deleteDeptByUserId(List<Long> userIds) {
        userDeptService.delete(Wrappers.<UserDeptEntity>lambdaUpdate()
                .in(UserDeptEntity::getUserId, userIds));
    }

    /**
     * 根据用户ID删除部门信息
     *
     * @param userId 用户ID
     */
    private void deleteDeptByUserId(Long userId) {
        deleteDeptByUserId(Collections.singletonList(userId));
    }

    /**
     * 根据用户ID删除部门信息
     *
     * @param userIds 用户ID
     */
    private void deleteRoleByUserId(List<Long> userIds) {
        userRoleService.delete(Wrappers.<UserRoleEntity>lambdaUpdate()
                .in(UserRoleEntity::getUserId, userIds));
    }

    /**
     * 根据用户ID删除部门信息
     *
     * @param userId 用户ID
     */
    private void deleteRoleByUserId(Long userId) {
        deleteDeptByUserId(Collections.singletonList(userId));
    }

    /**
     * 查询用户拥有的所有角色ID
     *
     * @param userId 用户ID
     * @return 角色列表
     */
    private Set<Long> selectRoleIds(Long userId) {
        return userRoleService.listDistinctObjs(Wrappers.<UserRoleEntity>lambdaQuery()
                .select(UserRoleEntity::getRoleId)
                .eq(UserRoleEntity::getUserId, userId), UserRoleEntity::getRoleId);
    }

    /**
     * 查询用户拥有的所有部门ID
     *
     * @param userId 用户ID
     * @return 部门列表
     */
    private Set<Long> selectDeptIds(Long userId) {
        return userDeptService.listDistinctObjs(Wrappers.<UserDeptEntity>lambdaQuery()
                .select(UserDeptEntity::getDeptId)
                .eq(UserDeptEntity::getUserId, userId), UserDeptEntity::getDeptId);
    }

    /**
     * 删除用户
     *
     * @param userIds 用户ID
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void deleteUser(List<Long> userIds) {
        deleteBatch(userIds);
        // 删除部门关系
        deleteDeptByUserId(userIds);
        // 删除角色关系
        deleteRoleByUserId(userIds);
    }

    /**
     * 冻结用户
     *
     * @param ids 用户ID
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void disable(List<Long> ids) {
        update(Wrappers.<UserEntity>lambdaUpdate()
                .set(UserEntity::getStatus, EnableStatus.DISABLE.getCode())
                .in(UserEntity::getId, ids)
                .eq(UserEntity::getStatus, EnableStatus.ENABLE.getCode()));
    }

    /**
     * 解冻用户
     *
     * @param ids 用户ID
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void enable(List<Long> ids) {
        update(Wrappers.<UserEntity>lambdaUpdate()
                .set(UserEntity::getStatus, EnableStatus.ENABLE.getCode())
                .in(UserEntity::getId, ids)
                .eq(UserEntity::getStatus, EnableStatus.DISABLE.getCode()));
    }

    /**
     * 根据用户名查询用户信息
     *
     * @param username 用户名
     * @return 用户信息
     */
    public UserEntity getUserByUsername(String username) {
        return getOne(Wrappers.<UserEntity>lambdaQuery()
                .eq(UserEntity::getUsername, username));
    }

    /**
     * 当前用户的角色列表
     */
    public Set<Long> roleIds(Long userId) {
        return userRoleService.listDistinctObjs(Wrappers.<UserRoleEntity>query()
                .select("distinct role_id")
                .lambda().setEntityClass(UserRoleEntity.class)
                .eq(UserRoleEntity::getUserId, userId), UserRoleEntity::getRoleId);
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void updatePassword(Long userId, String oldPwd, String newPwd) {
        UserEntity user = checkActive(userId);
        String pwd = DigestUtils.sha256Hex(oldPwd + user.getSalt());
        Preconditions.check(() -> !Objects.equals(user.getPassword(), pwd), ErrorCode.PROFILE_OLD_PWD_FAIL);
        UserEntity data = UserEntity.builder()
                .id(user.getId())
                .password(DigestUtils.sha256Hex(newPwd + user.getSalt()))
                .build();
        update(data);
    }

}
